/* JavaLog - package tracker 
 * 
 * Copyright (C) 2009  Allyson Lima, Diego Pedro, Victor Freire
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software 
 * Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this 
 * program.  If not, see <http://www.gnu.org/licenses/>.
 */

package br.edu.ufcg.ccc.javalog;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import br.edu.ufcg.ccc.javalog.allshortestpath.FloydWarshall;
import br.edu.ufcg.ccc.javalog.exceptions.PackageException;
import br.edu.ufcg.ccc.javalog.exceptions.StoreException;

/**
 * Represents a generic store.
 * 
 * @author Allyson Lima, Diego Pedro, Victor Freire
 * @version 08/11/09
 */
public abstract class Store {
	private static Map<String, Package> allPackages; // PackageID - Package
	
	// TODO add set packages
	//private Set<Package> packagesInTransit; // packages here just passing by,
											// waiting to be forwared to the
											// next store
	//private Set<Package> packagesToBeDelivered; // packages here that are
												// waiting to be delivered to
												// their owners
	private String name;
	private Address address;
	private Store airport;
	private int packagesHandled;
	private int sales;
	private List<Store> nearestStores;
	private int vertex;
	private ArrayList<Integer> collectionStoresInShortestPath;
	private static int indexNextShortestPath = 0;
	protected FloydWarshall distances;
	protected Map<Integer, Franchise> mapVertexFranchise;

	/**
	 * Constructs a new Store.
	 * 
	 * @param name
	 *            the name of the store
	 * @param address
	 *            the address of the store
	 * @param airport
	 *            does the store has an airport?
	 * @throws IllegalArgumentException
	 *             parameters that failed default validation by Validator
	 */
	public Store(String name, Address address, boolean airport)
			throws StoreException {
		Validator.validatePhrase(name, new StoreException(
				"name validation failed"));
		Validator.validateObject(address, new StoreException(
				"address validation failed"));
		this.name = name;
		this.address = address;
		// TODO write getNearestAirport()
		// if(airport) airport = getNearestAirport();

		// TODO calculate nearestStores
	}

	/**
	 * Return the vertex of the graph
	 * 
	 * @return the vertex of the graph
	 */
	public int getVertex() {
		return vertex;
	}

	/**
	 * Modifies the vertex of the graph
	 * 
	 * @param vertex
	 *            Vertex of the graph
	 */
	public void setVertex(int vertex) {
		this.vertex = vertex;
	}

	/**
	 * Creates array with shortest path
	 * 
	 * @param destinationStore
	 *            Destination Store
	 */
	public void createShortestPath(Store destinationStore) {
		collectionStoresInShortestPath = distances.getShortestPath(getVertex(),
				destinationStore.getVertex());
	}

	/**
	 * Returns next store in shortest path
	 * 
	 * @param destinationStore
	 *            Destination Store
	 * @return next store in shortest path
	 */
	public Store getNextStoreInShortestPath(Store destinationStore)
			throws IndexOutOfBoundsException {
		return mapVertexFranchise.get(collectionStoresInShortestPath
				.get(++indexNextShortestPath));
	}

	/**
	 * Returns the map relating package ids and packages.
	 */
	public static Map<String, Package> getPackages() {
		return allPackages;
	}

	/**
	 * Notifies this Store of the arrival of a new package. Retrivies the
	 * package by its ID and calls receivePackage(Package pkg).
	 * 
	 * @param packageID
	 *            the id of the package received
	 * @throws PackageException
	 *             if the package with the ID specified cannot be found
	 */
	public void receivePackage(String packageID) throws PackageException {
		Validator.validatePackageID(packageID, new PackageException(
				"invalid packageID"));
		Package pkg = allPackages.get(packageID);
		Validator.validateObject(pkg, new PackageException(
				"the package requested was not found!"));
		receivePackage(pkg);
	}

	/**
	 * Notifies this Store of the arrival of a new package.
	 * 
	 * @param pkg
	 *            the package received
	 * @throws PackageException
	 *             when a null package is passed
	 */
	public void receivePackage(Package pkg) throws PackageException {
		Validator.validateObject(pkg, new PackageException(
				"pkg validation failed"));

		// Update packages handled counter.
		++packagesHandled;

		//TODO Update package tracking report
	}

	// TODO finish createPackage()
	public void createPackage() {
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		Validator.validatePhrase(name, new IllegalArgumentException(
				"name validation failed"));
		this.name = name;
	}

	/**
	 * @return the address
	 */
	public Address getAddress() {
		return address;
	}

	/**
	 * @param address
	 *            the address to set
	 */
	public void setAddress(Address address) {
		Validator.validateObject(address, new IllegalArgumentException(
				"address validation failed"));
		this.address = address;
	}

	/**
	 * Does this Store has an airport?
	 * 
	 * @return true if it has an airport, false otherwise 
	 */
	public boolean hasAirport() {
		return (airport == null);
	}

	/**
	 * Returns the nearest airport if this Store does not have one or null if it
	 * does have one.
	 */
	public Store getNearestAirport() {
		return airport;
	}

	/**
	 * Update airport existence status.
	 * 
	 * @param hasAirport
	 *            does this franchise has an airport?
	 */
	public void setHasAirport(boolean hasAirport) {
		if (hasAirport) {
			this.airport = null;
		} else {
			// TODO setHasAirport() - get nearest airport when airport is
			// removed
		}
	}

	/**
	 * @return the packagesHandled
	 */
	public int getPackagesHandled() {
		return packagesHandled;
	}

	/**
	 * @return the sales
	 */
	public int getSales() {
		return sales;
	}

	/**
	 * @return the nearestStores
	 */
	public List<Store> getNearestStores() {
		return nearestStores;
	}

	@Override
	public String toString() {
		return String.format("Nome: %s%nEndereco: %n%s%nTem aeroporto: %s%n",
				name, address, (airport != null ? "sim" : "nao"));
	}

}
